package cn.xinfei.xdecision.data.core.frame.executors.groovy;


import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import groovy.lang.Binding;
import groovy.lang.GroovyShell;
import groovy.lang.Script;
import groovy.util.Eval;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
public class GroovySupport {
    private static final Object lock = new Object();
    private static final GroovyShell shell;

    private static Map<String,Script> cache = Maps.newConcurrentMap();

    static {
        CompilerConfiguration cfg = new CompilerConfiguration();
        cfg.setScriptBaseClass(MyBasicScript.class.getName());
        shell = new GroovyShell(cfg);
    }

    public Object parseExpr(String expr) {
        Script s = getScriptFromCache(expr);
        return s.run();
    }

    public Object parseExpr(String expr, Map<?, ?> map) {
        Binding binding = new Binding(map);
        Script script = getScriptFromCache(expr);
        synchronized (script) {
            script.setBinding(binding);
            return script.run();
        }
    }

    public Object parseExprWithAt(String expr, Map<?, ?> map) {
        Binding binding = new Binding(map);
        Script script = getScriptFromCache(expr.replaceAll("@",""));
        synchronized (script) {
            script.setBinding(binding);
            return script.run();
        }
    }

    public Object parseExpr(String expr, Binding binding) {
        Script script = getScriptFromCache(expr);
        synchronized (script) {
            script.setBinding(binding);
            return script.run();
        }
    }
    public Object parseExprWithAt(String expr, Binding binding) {
        Script script = getScriptFromCache(expr.replaceAll("@",""));
        synchronized (script) {
            script.setBinding(binding);
            return script.run();
        }
    }

    private Script getScriptFromCache(String expr) {
        if (cache.containsKey(expr)) {
            return cache.get(expr);
        }
        synchronized (lock) {
            if (cache.containsKey(expr)) {
                return cache.get(expr);
            }
            Script script = shell.parse(expr);
            cache.put(expr, script);
            return script;
        }
    }


    /**
     * @param args
     */
    public static void main(String[] args) {

        // eg. get one row from db
        Map<String, Object> row = new HashMap<String, Object>();
        row.put("id", 42);
        row.put("threeAttribute", true);
        row.put("a", 5.5);
        row.put("b", 10);

        GroovySupport exprSupport = new GroovySupport();

        //带绑定数据参数的调用方式
//        System.out.println(exprSupport.parseExpr("nvl(id,0)", row));
//        System.out.println(exprSupport.parseExpr("nvl(name,'anonymous')", row));

        //不带绑定数据参数的调用方式，这个是groovy的内置能力
        System.out.println("withAt***" + exprSupport.parseExprWithAt("@a@ / @b@ < 2  || @threeAttribute@ == false", row));
        System.out.println("withAt2***" + exprSupport.parseExprWithAt("@a@ + @a@", row));

        // 获取因子
        row.put("age", 20);

        int minAge = 18;
        int maxAge = 40;
        Boolean result = Boolean.parseBoolean(exprSupport.parseExpr("age >= " + minAge + " && age <= " + maxAge, row).toString());
        System.out.println("result = " + result);

        row.put("a", "abc");
        System.out.println("=== " + exprSupport.parseExpr("a == \"abc\" || 2 < 0 || threeAttribute == false", row));

        Double score;
        int baseScore = 20;
        Double coefficient = 5.2;
        int ipTimes = 7;
        if (result) {
            score = Double.parseDouble(Eval.xyz(baseScore, coefficient, ipTimes, "x + y * z").toString());
        } else {
            score = 0d;
        }
        System.out.println("rule score = " + score);

        Set<String> a = new HashSet<>();
        a.add("a");
        a.add("b");

        Set<String> b = new HashSet<>();
        b.add("a");
        b.add("b");
        b.add("c");
        Set<String> a_b = Sets.difference(a, b);
        Set<String> b_a = Sets.difference(b, a);
        System.out.println("a = " + a);
        System.out.println("b = " + b);
        System.out.println("a_b = " + a_b);
        System.out.println("b_a = " + b_a);


    }


}
